package com.xiyuan.smartutils;

import com.xiyuan.smartutils.comparator.LengthComparator;
import com.xiyuan.smartutils.constants.RegexpConstants;
import com.xiyuan.smartutils.exceptions.UtilException;
import com.xiyuan.smartutils.function.SFunc1;

import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则表表达式
 *
 * @author xiyuan-lgz 2024-07-20 @version v1.0.0 新建与整理
 */
@SuppressWarnings({"UnnecessaryUnicodeEscape"})
public class Regexps implements RegexpConstants {
    
    private static final Pattern GROUP_VAR = Pattern.compile("\\$(\\d+)");
    
    /**
     * 正则中需要被转义的关键字
     */
    public final static Set<Character> RE_KEYS = Arrays.newHashSet('$',
                                                                   '(',
                                                                   ')',
                                                                   '*',
                                                                   '+',
                                                                   '.',
                                                                   '[',
                                                                   ']',
                                                                   '?',
                                                                   '\\',
                                                                   '^',
                                                                   '{',
                                                                   '}',
                                                                   '|');
    
    /**
     * 给定内容是否匹配正则
     *
     * @param regex   正则
     * @param content 内容
     * @return 正则为null或者""则不检查，返回true，内容为null返回false
     */
    public static boolean isMatch(String regex, CharSequence content) {
        if (content == null) {
            // 提供null的字符串为不匹配
            return false;
        }
        
        // 正则不存在则为全匹配
        return StrUtils.isEmpty(regex) || isMatch(Pattern.compile(regex, Pattern.DOTALL), content);
    }
    
    
    /**
     * 取得内容中匹配的所有结果
     *
     * @param pattern 编译后的正则模式
     * @param content 被查找的内容
     * @param group   正则的分组
     * @return 结果列表
     * 
     */
    public static List<String> findAll(Pattern pattern, CharSequence content, int group) {
        return findAll(pattern, content, group, new ArrayList<>());
    }
    
    /**
     * 取得内容中匹配的所有结果
     *
     * @param <T>        集合类型
     * @param pattern    编译后的正则模式
     * @param content    被查找的内容
     * @param group      正则的分组
     * @param collection 返回的集合类型
     * @return 结果集
     */
    public static <T extends Collection<String>> T findAll(Pattern pattern, CharSequence content, int group,
                                                           T collection) {
        if (null == pattern || null == content) {
            return null;
        }
        Asserts.nonNull(collection, "Collection must be not null !");
        
        findAll(pattern, content, (matcher) -> collection.add(matcher.group(group)));
        return collection;
    }
    
    /**
     * 取得内容中匹配的所有结果，使用{@link Consumer}完成匹配结果处理
     *
     * @param pattern  编译后的正则模式
     * @param content  被查找的内容
     * @param consumer 匹配结果处理函数
     * 
     */
    public static void findAll(Pattern pattern, CharSequence content, Consumer<Matcher> consumer) {
        if (null == pattern || null == content) {
            return;
        }
        
        final Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            consumer.accept(matcher);
        }
    }
    
    /**
     * 给定内容是否匹配正则
     *
     * @param pattern 模式
     * @param content 内容
     * @return 正则为null或者""则不检查，返回true，内容为null返回false
     */
    public static boolean isMatch(Pattern pattern, CharSequence content) {
        // 提供null的字符串为不匹配
        if (content == null || pattern == null) return false;
        return pattern.matcher(content).matches();
    }
    
    /**
     * 正则替换指定值<br>
     * 通过正则查找到字符串，然后把匹配到的字符串加入到replacementTemplate中，$1表示分组1的字符串
     *
     * <p>
     * 例如：原字符串是：中文1234，我想把1234换成(1234)，则可以：
     *
     * <pre>
     * ReUtil.replaceAll("中文1234", "(\\d+)", "($1)"))
     *
     * 结果：中文(1234)
     * </pre>
     *
     * @param content             文本
     * @param regex               正则
     * @param replacementTemplate 替换的文本模板，可以使用$1类似的变量提取正则匹配出的内容
     * @return 处理后的文本
     */
    public static String replaceAll(CharSequence content, String regex, String replacementTemplate) {
        final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
        return replaceAll(content, pattern, replacementTemplate);
    }
    
    /**
     * 正则替换指定值<br>
     * 通过正则查找到字符串，然后把匹配到的字符串加入到replacementTemplate中，$1表示分组1的字符串
     *
     * @param content             文本
     * @param pattern             {@link Pattern}
     * @param replacementTemplate 替换的文本模板，可以使用$1类似的变量提取正则匹配出的内容
     * @return 处理后的文本
     * 
     */
    public static String replaceAll(CharSequence content, Pattern pattern, String replacementTemplate) {
        if (StrUtils.isEmpty(content)) return content == null ? null : content.toString();
        
        final Matcher matcher = pattern.matcher(content);
        boolean result = matcher.find();
        if (result) {
            final Set<String> varNums = findAll(GROUP_VAR,
                                                replacementTemplate,
                                                1,
                                                new TreeSet<>(LengthComparator.INSTANCE.reversed()));
            final StringBuffer sb = new StringBuffer();
            do {
                String replacement = replacementTemplate;
                for (final String var : varNums) {
                    final int group = Integer.parseInt(var);
                    replacement = replacement.replace("$" + var, matcher.group(group));
                }
                matcher.appendReplacement(sb, escape(replacement));
                result = matcher.find();
            }
            while (result);
            matcher.appendTail(sb);
            return sb.toString();
        }
        return content == null ? null : content.toString();
    }
    
    /**
     * 替换所有正则匹配的文本，并使用自定义函数决定如何替换<br>
     * replaceFun可以通过{@link Matcher}提取出匹配到的内容的不同部分，然后经过重新处理、组装变成新的内容放回原位。
     *
     * <pre class="code">
     *     replaceAll(this.content, "(\\d+)", parameters -&gt; "-" + parameters.group(1) + "-")
     *     // 结果为："ZZZaaabbbccc中文-1234-"
     * </pre>
     *
     * @param str        要替换的字符串
     * @param regex      用于匹配的正则式
     * @param replaceFun 决定如何替换的函数
     * @return 替换后的文本
     * 
     */
    public static String replaceAll(CharSequence str, String regex, SFunc1<Matcher, String> replaceFun) {
        return replaceAll(str, Pattern.compile(regex), replaceFun);
    }
    
    /**
     * 替换所有正则匹配的文本，并使用自定义函数决定如何替换<br>
     * replaceFun可以通过{@link Matcher}提取出匹配到的内容的不同部分，然后经过重新处理、组装变成新的内容放回原位。
     *
     * <pre class="code">
     *     replaceAll(this.content, "(\\d+)", parameters -&gt; "-" + parameters.group(1) + "-")
     *     // 结果为："ZZZaaabbbccc中文-1234-"
     * </pre>
     *
     * @param str        要替换的字符串
     * @param pattern    用于匹配的正则式
     * @param replaceFun 决定如何替换的函数,可能被多次调用（当有多个匹配时）
     * @return 替换后的字符串
     * 
     */
    public static String replaceAll(CharSequence str, Pattern pattern, SFunc1<Matcher, String> replaceFun) {
        if (StrUtils.isEmpty(str)) {
            return str == null ? null : str.toString();
        }
        
        final Matcher matcher = pattern.matcher(str);
        final StringBuffer buffer = new StringBuffer();
        while (matcher.find()) {
            try {
                matcher.appendReplacement(buffer, replaceFun.apply(matcher));
            }
            catch (Exception e) {
                throw new UtilException(e);
            }
        }
        matcher.appendTail(buffer);
        return buffer.toString();
    }
    
    /**
     * 转义字符，将正则的关键字转义
     *
     * @param c 字符
     * @return 转义后的文本
     */
    public static String escape(char c) {
        final StringBuilder builder = new StringBuilder();
        if (RE_KEYS.contains(c)) {
            builder.append('\\');
        }
        builder.append(c);
        return builder.toString();
    }
    
    /**
     * 转义字符串，将正则的关键字转义
     *
     * @param content 文本
     * @return 转义后的文本
     */
    public static String escape(CharSequence content) {
        if (StrUtils.isBlank(content)) {
            return content == null ? null : content.toString();
        }
        
        final StringBuilder builder = new StringBuilder();
        int len = content.length();
        char current;
        for (int i = 0; i < len; i++) {
            current = content.charAt(i);
            if (RE_KEYS.contains(current)) {
                builder.append('\\');
            }
            builder.append(current);
        }
        return builder.toString();
    }
    
}
